package com.longsichao.lscframe.ctrl;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

import com.longsichao.lscframe.app.LSCApplication;
import com.longsichao.lscframe.tools.FileManager;
import com.longsichao.lscframe.tools.LSCLog;

import java.io.File;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.Map;
import java.util.Set;

/**
 * @author SONG GUANYULONGSICHAO (leo.song[at]outlook[dot]com)
 * @version 1
 */
public final class CacheHelper {
    private static final String CACHE_TIME = "CacheTime";
    private static final String DISK_CACHE_PATH = "DiskCache";
    private static final String VALID_TIME = "ValidTime";

    /**
     * clear disk cache in SharedPreferences.
     *
     * @return {@code true} if cache has been cleared, {@code false} otherwise.
     */
    public static boolean clearDiskCache() {
        return getSharedPreferences().edit().clear().commit();
    }

    /**
     * clear and delete disk cache file in application's cache directory and external cache directory, if it exist.
     */
    public static void clearDiskCacheFile() {
        clearDiskCacheFile(LSCApplication.applicationContext.getCacheDir());
        if (FileManager.isSdCardExist()) {
            clearDiskCacheFile(LSCApplication.applicationContext.getExternalCacheDir());
        }
    }

    /**
     * Formatting cache size from bytes to string.
     *
     * @param size The size in bytes.
     * @return The size in string.
     */
    public static String formatCacheSize(long size) {
        String[] units = new String[]{"B", "KB", "MB", "GB", "TB", "PB"};
        double mod = 1024.0;
        int i = 0;
        while (size >= mod) {
            size /= mod;
            i++;
        }
        return new DecimalFormat("#.00").format(size) + " " + units[i];
    }

    /**
     * get disk cache in SharedPreferences.
     *
     * @param key API url
     * @return json string
     */
    public static String getDiskCache(String key) {
        if (isCacheTimeValid(key)) {
            SharedPreferences pref = getSharedPreferences();
            LSCLog.i("use disk cache with key : " + key);
            return pref.getString(key, null);
        }
        return null;
    }

    /**
     * Returns the size of cache file in bytes.
     *
     * @return The size of cache file in bytes.
     */
    public static long getDiskCacheFileSize() {
        long size = getDiskCacheFileSize(LSCApplication.applicationContext.getCacheDir());
        if (FileManager.isSdCardExist()) {
            size += getDiskCacheFileSize(LSCApplication.applicationContext.getExternalCacheDir());
        }
        LSCLog.v("Disk cache file size : " + size);
        return size;
    }

    /**
     * Returns the size of cache in bytes.
     *
     * @return The size of cache in bytes.
     */
    public static long getDiskCacheSize() {
        long size = 0;
        Map<String, ?> kvs = getSharedPreferences().getAll();
        if (kvs == null || kvs.isEmpty()) {
            return size;
        }
        Set<String> keys = kvs.keySet();
        for (String key : keys) {
            size += kvs.get(key).toString().getBytes(Charset.defaultCharset()).length;
        }
        LSCLog.v("Disk cache size : " + size);
        return size;
    }

    /**
     * set disk cache to SharedPreferences.
     *
     * @param key       API url
     * @param info      json string
     * @param validTime API cache valid time in milliseconds since January 1, 1970 00:00:00 UTC.
     */
    public static void setDiskCache(String key, String info, long validTime) {
        Editor editor = getSharedPreferences().edit();
        editor.putLong(key + CACHE_TIME, System.currentTimeMillis());
        editor.putLong(key + VALID_TIME, validTime);
        editor.putString(key, info);
        if (editor.commit()) {
            LSCLog.v("disk cache saved in key : " + key);
        } else {
            LSCLog.v("disk cache save failed in key : " + key);
        }
    }

    private static void clearDiskCacheFile(File file) {
        if (file!=null&&file.isFile()) {
            String fileName = file.getName();
            if (file.delete()) {
                LSCLog.v("delete disk cache file : " + fileName + " success.");
            } else {
                LSCLog.v("delete disk cache file : " + fileName + " fail.");
            }
        }
        if (file!=null&&file.isDirectory()) {
            File[] files = file.listFiles();
            if (files == null) {
                return;
            }
            for (File f : files) {
                clearDiskCacheFile(f);
            }
            String fileName = file.getName();
            if (file.delete()) {
                LSCLog.v("delete disk cache directory : " + fileName + " success.");
            } else {
                LSCLog.v("delete disk cache directory : " + fileName + " fail.");
            }
        }
    }

    private static long getDiskCacheFileSize(File file) {
        long size = 0;
        if (file!=null&&file.isFile()) {
            size += file.length();
        }
        if (file!=null&&file.isDirectory()) {
            File[] files = file.listFiles();
            if (files == null) {
                return 0;
            }
            for (File f : files) {
                size += getDiskCacheFileSize(f);
            }
        }
        return size;
    }

    private static SharedPreferences getSharedPreferences() {
        return LSCApplication.applicationContext.getSharedPreferences(DISK_CACHE_PATH, Context.MODE_MULTI_PROCESS);
    }

    private static Boolean isCacheTimeValid(String key) {
        SharedPreferences pref = getSharedPreferences();
        Long cacheTime = pref.getLong(key + CACHE_TIME, 0);
        Long validTime = pref.getLong(key + VALID_TIME, 0);
        Long currentTime = System.currentTimeMillis();
        Long period = currentTime - cacheTime;
        if (period < validTime) {
            LSCLog.i("in validity in key : " + key);
            return true;
        } else {
            LSCLog.i("out of validity in key : " + key);
            return false;
        }
    }

    private CacheHelper() {
    }
}
